﻿/*------------------------------------------------------------------------

RENDER_WIN32_OPENGL.HPP v1.2.9

Copyright 楊志賢 CxxlMan, 2012 - 2014
All Rights Reserved


------------------------------------------------------------------------*/

#if !defined(__RENDER_WIN32_OPENGL_HPP_Cxxl3D)
#define __RENDER_WIN32_OPENGL_HPP_Cxxl3D

#include <Window\Window_Win32\WINDOW_WIN32.HPP>
#include <Render\RENDER.HPP>


#include <GL\glew.h>    // 包含最新的gl.h,glu.h庫
#include <gl\glut.h>    // 包含OpenGL實用庫


// 元件介面的識別碼
#define IRender_Win32_OpenGL_ClassID L"1812028A591E43989FAF896BF0D3B912"


namespace Cxxl3D
{



// 作為 IRender_Win32_OpenGL::Init() 的參數
typedef bool (cxxlFASTCALL *InitOpenglFunc)();

// 每當一個 ICamera 循環前(已完成 ICamera 相關設定)，會呼叫繼承此介面的物件
class ICameraLoopReset:virtual public CxxlMan::cxxlObject
{
public:
  // Constructor
  ICameraLoopReset()
    :CxxlMan::cxxlObject(CxxlMan::Spirit_Easy)
  {}
  // Destructor
  virtual ~ICameraLoopReset(){}

  // 須實作，每當一個 ICamera 循環前會呼叫一次
  virtual void cxxlFASTCALL Reset() = 0;
};

class IDatagramDisposal_OpenGL;

class IRenderDatagramDisposalCenterOpengl:virtual public CxxlMan::cxxlObject
{
public:
  // Constructor
  IRenderDatagramDisposalCenterOpengl()
    :CxxlMan::cxxlObject(CxxlMan::Spirit_Easy)
  {}

  // Destructor
  virtual ~IRenderDatagramDisposalCenterOpengl()
  {}

  // 執行渲染，若 IRenderDatagram_Arg 對應的 IDatagramDisposal 不存在回覆 false
  virtual bool cxxlFASTCALL Exec(const CxxlMan::Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, const CxxlMan::CSmart_Ptr<Matrix4x4> &ViewMatrix_Arg) = 0;

  // 取得 ID 指定的 DD_OpenGL
  virtual CxxlMan::Smart_Ptr<IDatagramDisposal_OpenGL> cxxlFASTCALL GetIDatagramDisposal_OpenGL(CxxlMan::UNICODE_String ID) = 0;


// 頂點格式指定，可用 | 組合的值，交給 SetFVF()
#define FVF_XYZ 0x01       /* 使用 X Y Z 三維表示頂點位置 */
#define FVF_DIFFUSE 0x02   /* 使用 RGBA 四維表示頂點顏色 */
#define FVF_NORMAL 0x04    /* 頂點含有法向量 */
#define FVF_TEXTURE 0x08   /* 頂點含有紋理 uv */

  // 指定頂點格式
  virtual void cxxlFASTCALL SetFVF(int) = 0;

  // 放入 ICameraLoopReset 
  virtual void AddICameraLoopReset(const CxxlMan::Smart_Ptr<ICameraLoopReset> &ICameraLoopReset_Arg) = 0;
};

class IDatagramDisposal_OpenGL:public IDatagramDisposal
{
public:
  // Constructor
  IDatagramDisposal_OpenGL()
    :cxxlObject(CxxlMan::Spirit_Easy)
  {
  }

  // Destructor
  virtual ~IDatagramDisposal_OpenGL()
  {
  }

  // 若有子 IRenderDatagram 對應的 IDatagramDisposal 不存在回覆 false
  virtual bool cxxlFASTCALL Exec(const CxxlMan::Smart_Ptr<IRenderDatagramDisposalCenterOpengl> &IRenderDatagramDisposalCenter_Arg,
    const CxxlMan::Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, const CxxlMan::CSmart_Ptr<Matrix4x4> &ViewMatrix_Arg) = 0;
};

inline bool cxxlFASTCALL DefaultInitOpenglFunc()
{
  glShadeModel(GL_SMOOTH);              // 啟用陰影平滑
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	// 黑色背景

  glClearDepth(1.0f);                 // 設置深度緩存
  glEnable(GL_DEPTH_TEST);            // 啟用深度測試
  glDepthFunc(GL_LEQUAL);             // 所作深度測試的類型

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 告訴系統對透視進行修正

  return true;
}


class IRender_Win32_OpenGL:public IRender, public IWindowWin32_Observer
{
  virtual void cxxlFASTCALL SysInit(const CxxlMan::Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg) = 0;

protected:

  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F) = 0
  {
    if(F != NULL && IRender::Ref(F))
      return true;
    else
      return false;
  }

public:
  // Constructor
  IRender_Win32_OpenGL()
    :cxxlObject(CxxlMan::Spirit_Easy),
    cxxlPreserve2(IRender_Win32_OpenGL_ClassID)
  {

  }
  // Destructor
  virtual ~IRender_Win32_OpenGL(){}

  // 保存永續儲存資料
  virtual bool cxxlFASTCALL Save(const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg, const CxxlMan::UNICODE_String &BackupName) = 0;

  // 取回永續儲存資料，並立即執行，若有錯誤原資料不變
  virtual bool cxxlFASTCALL Load(const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg, const CxxlMan::UNICODE_String &BackupName) = 0;

  // 會呼叫 IWindow_Win32::Reset()
  virtual bool cxxlFASTCALL Reset() = 0;

  // 初始一些 OpenGL 內定設定
  virtual bool cxxlFASTCALL Init(InitOpenglFunc pInitOpenglFunc_Arg = DefaultInitOpenglFunc) = 0;


public:

  // 取得 IRender_Win32_OpenGL 物件，失敗傳回 NULL
  static CxxlMan::Smart_Ptr<IRender_Win32_OpenGL> cxxlFASTCALL Create(const CxxlMan::Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg = NULL)
  {
    CxxlMan::Smart_Ptr<IWindow_Win32> IWindow_Win32_Ptr = IWindow_Win32_Arg;
    if(IWindow_Win32_Arg.isNULL())
    {
      IWindow_Win32_Ptr = IWindow_Win32::Create();
      if(IWindow_Win32_Ptr.isNULL())
        return NULL;
      IWindow_Win32_Ptr->Reset();
    }

    CxxlMan::Smart_Ptr<IRender_Win32_OpenGL> r = 
      CxxlMan::Preserve2_Cast<IRender_Win32_OpenGL>( CxxlMan::cxxlCM_GetElement(IRender_Win32_OpenGL_ClassID, CXXL3DDEFAULTGROUP) );

    if(r.isNULL() != true)
    {
      r->SysInit(IWindow_Win32_Ptr);
      IWindow_Win32_Ptr->Attach((IRender_Win32_OpenGL*)r);
    }

    return r;
  }

  // 從永存資料產生 IRender_Win32_OpenGL 物件，並立即依保存的永存資料建立視窗
  // 失敗傳回 NULL
  static CxxlMan::Smart_Ptr<IRender_Win32_OpenGL> cxxlFASTCALL Create(const CxxlMan::UNICODE_String &BackupName, const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg)
  {
    CxxlMan::Smart_Ptr<cxxlPreserve2> cxxlPreserve2_Ptr(NULL);
    CxxlMan::REGENERARETURN_TYPE rt = cxxlCM_Regenera(cxxlPreserve2_Ptr, BackupName, IcxxlStorage_Arg, true);
    if(rt == CxxlMan::Regenera_Success)
    {
      CxxlMan::Smart_Ptr<IRender_Win32_OpenGL> IRender_Win32_OpenGL_Ptr = CxxlMan::Smart_Cast<IRender_Win32_OpenGL>(cxxlPreserve2_Ptr);
      if(IRender_Win32_OpenGL_Ptr.isNULL() == false && IRender_Win32_OpenGL_Ptr->Reset())
        return IRender_Win32_OpenGL_Ptr;
    }
    return NULL;
  }

  friend class CxxlMan::IcxxlStorage;
  friend class CxxlMan::Smart_Ptr<IRender_Win32_OpenGL>;
};



// 初始化，這裡只做元件註冊
inline void Render_Win32_OpenGL_Init()
{
  CxxlMan::cxxlCM_ElementReg(IRender_Win32_OpenGL_ClassID, CXXL3DDEFAULTGROUP, L"Render_Win32_OpenGL.dll");
}


}   /* namespace Cxxl3D */
#endif

